package pers.bc.utils.pub;


/**
 * @FileName AmountConversion.java
 * @author SakuraSora
 * @Email 1468071675@qq.com
 * @DateTime 2019年1月16日 上午11:34:53
 * 
 * @Description <p>
 *              金额转换：数字金额转大写。（金额可以是整数也可以是小数；小数部分两位数，整数部分最大长度为16位）
 *              </p>
 *              <p>
 *              转换规则：<br>
 *              中文大写金额数字到"元"为止的，在"元"之后，应写"整"(或"正")字。<br>
 *              在"角"之后，可以不写"整"(或"正")字。大写金额数字有"分"的，"分"后面不写"整"(或"正")字。<br>
 *              中文大写金额数字前应标明"人民币"字样。<br>
 *              阿拉伯数字中间有"0"时，中文大写要写"零"字。<br>
 *              阿拉伯数字中间连续有几个"0"时，中文大写金额中间可以只写一个"零"字。<br>
 *              阿拉伯金额数字万位和元位是"0"；或者数字中间连续有几个"0"，万位、元位也是"0"，但千位、角位不是"0"时，中文大写金额中可以只写一个零字，也可以不写"零"字。<br>
 *              阿拉伯金额数字角位是"0"，而分位不是"0"时，中文大写金额"元"后面应写"零"字。
 *              </p>
 */
public class AmountConverUtil
{
    
    /**
     * 匹配由最大16位整数和2位小数组成合法金额的字符串的正则表达式
     */
    private static String sPatternMoney = "(^[1-9][0-9]{0,15}$)|((^[1-9][0-9]{0,15}[.][0-9]{1,2})$)|(^0[.][0-9]{1,2}$)|(^0{1}$)";
    
    /**
     * 存放大写金额数字0-9的字符串数组（常量）
     */
    private final static String[] UPPERCASE_NUMBERS = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
    /**
     * 存放整数大写金额单位（元(圆)到千兆）的字符串数组（常量）
     */
    private final static String[] AMOUNT_IUNITS = {"元", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "兆", "拾", "佰", "仟"};
    /**
     * 存放金额转换结果（大写金额）
     */
    private static String sAmountInWords;
    private static ThreadLocal<AmountConverUtil> amountutil = new ThreadLocal<AmountConverUtil>();

    public static AmountConverUtil getInstance() {
        AmountConverUtil df = amountutil.get();
        if (null == df) {
            synchronized (AmountConverUtil.class) {
                if (null == df) {
                    df = new AmountConverUtil();
                    amountutil.set(df);
                }
            }
        }
        return df;
    }
    
    private AmountConverUtil()
    {
        
    }
    
    /**
     * 获得金额转换结果
     * @param sMoney
     *            金额字符串
     * @return 大写金额
     * @throws Exception
     */
    public static  String getAmountInWords(String sMoney) throws Exception
    {
        sAmountInWords = ""; // 转换结果
        
        if (isMoneyNotEmpty(sMoney) == false || moneyCheck(sMoney) == false) return sAmountInWords;
        
        /**
         * 如果金额字符串包含小数点，分割整数与小数部分；否则转换整数部分并添加“整”字；<br>
         * 包含小数点：<br>
         * 如果小数部分为0，转换整数后添加“整”字；<br>
         * 如果整数和小数部分都为0，转换结果为“人民币零元整”<br>
         * 否则，整数转换结果拼接小数转换结果<br>
         * 最终结果开头要添加“人民币”
         */
        if (sMoney.contains("."))
        {
            String[] money = sMoney.split("\\.");
            if ((money[0].length() == 1 && Integer.parseInt(money[0]) == 0) && Integer.parseInt(money[1]) == 0)
            {
                sAmountInWords = "人民币" + "零元" + "整";
                return sAmountInWords;
            }
            else
            {
                if (!(money[0].length() == 1 && Integer.parseInt(money[0]) == 0))
                {
                    sAmountInWords += getIntegerAmountInWords(money[0]);
                }
                if (Integer.parseInt(money[1]) == 0)
                {
                    sAmountInWords += "整";
                    sAmountInWords = "人民币" + sAmountInWords;
                    return sAmountInWords;
                }
                if (Integer.parseInt(money[1]) != 0)
                {
                    sAmountInWords += getDecimalAmountInWords(money[1]);
                    sAmountInWords = "人民币" + sAmountInWords;
                    return sAmountInWords;
                }
            }
        }
        else
        {
            if (sMoney.length() == 1 && Integer.parseInt(sMoney) == 0)
            {
                sAmountInWords = "人民币" + "零元整";
            }
            else
            {
                sAmountInWords = "人民币" + getIntegerAmountInWords(sMoney) + "整";
            }
            return sAmountInWords;
        }
        return sAmountInWords;
    }
    
    /**
     * 整数部分金额转换;不考虑0元
     * @param sIntegerMoney
     *            整数部分金额（小写）
     * @return 整数部分大写金额
     */
    private static String getIntegerAmountInWords(String sIntegerMoney)
    {
        String sIntegerAmountInWords = "";
        int iNext;
        
        /**
         * 遍历： 1、非0数字转大写并加上单位<br>
         * 2、对0的处理（下标为i的字符为0）：<br>
         * （1）如：100,0000,0001等中间的一组4个0不做任何处理，直接跳过<br>
         * （2）“拾”位低一位为0， 不加“零”，直接加单位<br>
         * （3）其他情况，0（当前i下标字符为0）遇非0时补“零”
         */
        for (int i = 0, j = sIntegerMoney.length() - 1; i < sIntegerMoney.length(); i++, j--)
        {
            if (sIntegerMoney.charAt(i) != '0')
            {
                sIntegerAmountInWords += UPPERCASE_NUMBERS[sIntegerMoney.charAt(i) - '0'] + AMOUNT_IUNITS[j];
            }
            else
            {
                if (i + 4 < sIntegerMoney.length() && AMOUNT_IUNITS[j].equals("仟") && sIntegerMoney.substring(i, i + 4).equals("0000"))
                {
                    i = i + 3;
                    j = j - 3;
                    continue;
                }
                
                iNext = i + 1;
                if (iNext == sIntegerMoney.length())
                {
                    iNext = i;
                }
                
                if (AMOUNT_IUNITS[j + 1].equals("拾"))
                {
                    sIntegerAmountInWords += AMOUNT_IUNITS[j];
                }
                else if (sIntegerMoney.charAt(iNext) != '0')
                {
                    sIntegerAmountInWords += "零";
                }
            }
        }
        return sIntegerAmountInWords;
    }
    
    /**
     * 小数部分金额转换 ：到“分”为止；不考虑0角0分<br>
     * 效果如：0.09转换为 人民币玖分
     * @param sDecimalMoney
     *            小数部分金额（小写）
     * @return 小数部分大写金额
     */
    private static String getDecimalAmountInWords(String sDecimalMoney)
    {
        String sDecimalAmountInWords = "";
        
        /**
         * 1、阿拉伯金额数字角位不是‘0’，数字转大写并添加“角”<br>
         * 2、如果存在“分”位，并且分位不是‘0’，数字转大写并添加“分”
         */
        if (sDecimalMoney.length() > 0)
        {
            if (sDecimalMoney.charAt(0) != '0')
            {
                sDecimalAmountInWords += UPPERCASE_NUMBERS[sDecimalMoney.charAt(0) - '0'] + "角";
            }
            if (sDecimalMoney.length() > 1 && sDecimalMoney.charAt(1) != '0')
            {
                sDecimalAmountInWords += UPPERCASE_NUMBERS[sDecimalMoney.charAt(1) - '0'] + "分";
            }
        }
        return sDecimalAmountInWords;
    }
    
    /**
     * 使用正则表达式匹配金额格式（包括长度限定）：<br>
     * 判断传入的字符串是不是由最大16位整数和2位小数组成的合法金额（金额可以是整数）
     * @param sMoney
     *            金额字符串
     * @return 匹配--true<br>
     *         不匹配--false<br>
     * @throws Exception
     */
    public static boolean moneyPattern(String sMoney) throws Exception
    {
        if (sMoney.matches(sPatternMoney)) return true;
        else
            throw new Exception("输入金额不合法！");
    }
    
    /**
     * 检查金额格式（不使用使用正则表达式）：<br>
     * 判断传入的字符串是不是由最大16位整数和2位小数组成的合法金额（金额可以是整数）
     * @param sMoney
     *            金额字符串
     * @return 匹配--true<br>
     *         不匹配--false<br>
     * @throws Exception
     */
    public static boolean moneyCheck(String sMoney) throws Exception
    {
        boolean result = true;
        int count = 0;
        
        /**
         * 1、如果包含数字和小数点以外的字符，抛出异常<br>
         * 2、小数点只能出现一次<br>
         * 3、第一个和最后一个字符只能是数字<br>
         * 4、整数部分长度大于1（2位以上）时不能以0开头（分有小数点和没小数点的情况）<br>
         * 5、长度超出限制，抛出异常
         */
        for (int i = 0; i < sMoney.length(); i++)
        {
            if (sMoney.charAt(i) != '.' && (sMoney.charAt(i) < '0' || sMoney.charAt(i) > '9'))
            {
                result = false;
                throw new Exception("不是合法金额！");
            }
            if (sMoney.charAt(i) == '.') count++;
        }
        if (count > 1)
        {
            result = false;
            throw new Exception("不是合法金额！");
        }
        
        if ((sMoney.charAt(0) < '0' || sMoney.charAt(0) > '9')
                || (sMoney.charAt(sMoney.length() - 1) < '0' || sMoney.charAt(sMoney.length() - 1) > '9'))
        {
            result = false;
            throw new Exception("不是合法金额！");
        }
        
        if (sMoney.contains("."))
        {
            String[] money = sMoney.split("\\.");
            if (money[0].length() >= 2 && sMoney.charAt(0) == '0')
            {
                result = false;
                throw new Exception("不是合法金额！");
            }
        }
        else
        {
            if (sMoney.length() >= 2 && sMoney.charAt(0) == '0')
            {
                result = false;
                throw new Exception("不是合法金额！");
            }
        }
        
        if (sMoney.contains("."))
        {
            String[] money = sMoney.split("\\.");
            if (money[0].length() > 16)
            {
                result = false;
                throw new Exception("数值过大无法计算！");
            }
            if (money[1].length() > 2)
            {
                System.out.println("警告：转换时将舍去“分”以后的数值！");
            }
        }
        else
        {
            if (sMoney.length() > 16)
            {
                result = false;
                throw new Exception("数值过大无法计算！");
            }
        }
        return result;
    }
    
    /**
     * 判断传入的金额字符串是否不为空
     * @param sMoney
     *            金额字符串
     * @return 不为空--true<br>
     *         为空--false<br>
     * @throws Exception
     */
    public static boolean isMoneyNotEmpty(String sMoney) throws Exception
    {
        if (sMoney != null && !sMoney.equals("")) return true;
        else
            throw new Exception("金额为空！");
    }
    
    // /**
    // * 小数部分金额转换 ：到“分”为止；不考虑0角0分<br>
    // * 效果如：0.09转换为 人民币零玖分
    // * @param sDecimalMoney 小数部分金额（小写）
    // * @return 小数部分大写金额
    // */
    // private String getDecimalAmountInWords(String sDecimalMoney) {
    // String sDecimalAmountInWords = "";
    //
    // /**
    // * 1、阿拉伯金额数字角位不是‘0’，数字转大写并添加“角”<br>
    // * 2、如果存在“分”位： 分位不是‘0’，数字转大写并添加“分”<br>
    // * 如果“角”位是0并且“分“位不是0时，”分“位前写”零“字
    // */
    // if (sDecimalMoney.length() > 0) {
    // if (sDecimalMoney.charAt(0) != '0') {
    // sDecimalAmountInWords += UPPERCASE_NUMBERS[sDecimalMoney.charAt(0) - '0'] + "角";
    // }
    // if (sDecimalMoney.length() > 1) {
    // if (sDecimalMoney.charAt(1) != '0') {
    // sDecimalAmountInWords += UPPERCASE_NUMBERS[sDecimalMoney.charAt(1) - '0'] + "分";
    // if (sDecimalMoney.charAt(0) == '0') {
    // sDecimalAmountInWords = "零" + sDecimalAmountInWords;
    // }
    // }
    //
    // }
    // }
    // return sDecimalAmountInWords;
    // }
}
